use std::{collections::HashMap, env, io::Write, net::{IpAddr, SocketAddr}, path::Path, sync::{Arc, Mutex}, time::SystemTime};

use askama::Template;
use axum::{extract::{ DefaultBodyLimit, Multipart}, http::HeaderMap, response::Html, routing::get, Form, Json, Router};
use file_hashing::get_hash_file;
use md5::{Md5, Digest};
use memmap::MmapMut;
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use tower_http::limit::RequestBodyLimitLayer;
use tower_http::services::ServeDir;

/// 允许上传的大小
const MAX_UPLOAD_SIZE: usize = 8196 * 1024 * 1024; // 8196mb

const MAX_CHUNK_SIZE: u64 = 1 * 1024 * 1024;    //分片大小 1mb

// const MAX_CHUNK_SIZE: u64 = 512 * 1024;    //分片大小 1mb

static mut PROGRESS_BAR: Lazy<Arc<std::sync::Mutex<i32>>> = Lazy::new(|| {
    Arc::new(Mutex::new(0))
  });

#[tokio::main]
async fn main() {

    // let test = test().await;
    // println!("xxxx........");
    // 获取本机的IP地址
    let local_ip: IpAddr = get_local_ip().unwrap();
    
    // 构造要连接的SocketAddr
    let addr_string = format!("{}:5002", local_ip);
    let _addr: SocketAddr = addr_string.parse().unwrap();
    
    println!("Local IP: {}", local_ip);

    let mut port = 3000;

    // let ss = String::from("value");
    // let xx = &ss[..];

    //指定运行端口
    let args = std::env::args().collect::<Vec<String>>();
    if args.len() > 1 {
        port = args[1].parse::<i32>().expect(&format!("not a port : {}", &args[1]));
    }

    let bing_str = format!("0.0.0.0:{}",port);

    let app = Router::new()
            .route("/",get(hello_text))
            .route("/json",get(hello_json))
            .route("/html", get(hello_html))
            .route("/form", get(render_form).post(handle_form_submit))
            .route("/upload", get(upload_file).post(upload_file_action_chunk))
                .layer(DefaultBodyLimit::disable())
                .layer(RequestBodyLimitLayer::new(
                    MAX_UPLOAD_SIZE, /* 250mb */
                ))
                .layer(tower_http::trace::TraceLayer::new_for_http())
            .nest_service("/static", ServeDir::new("templates/static"))
            .route("/progress_bar", get(progress_bar))
            ;

    let listener = tokio::net::TcpListener::bind(bing_str).await.unwrap();
    let curr_path = env::current_dir().unwrap();
    println!("启动服务完成...");
    println!("当前目录:{:?}",curr_path);
    println!("请在浏览器访问 http://{}:{}",local_ip,port);
    axum::serve(listener, app).await.unwrap();
    println!("启动服务完成...");
}

fn get_local_ip() -> Result<IpAddr, Box<dyn std::error::Error>> {
    // 使用网络库来获取本机的IP地址
    let socket = std::net::UdpSocket::bind("0.0.0.0:0")?;
    socket.connect("8.8.8.8:80")?;
    let local_ip = socket.local_addr()?.ip();
    
    Ok(local_ip)
}

async fn test() {
    println!("test async...");
}

async fn progress_bar() -> String {
    let mut num = 0_i32;
    unsafe {
        let mut locl = PROGRESS_BAR.lock().unwrap();
        *locl += 1;
        num = *locl;
    }
    
    format!("{}",num)
}

async fn hello_text() -> &'static str {
    "Hello World!"
}

async fn hello_json() -> Json<Value> {
    Json(json!({"name":"张三","age":18}))
}

async fn hello_html() -> Html<&'static str> {
    Html("<h1>Hello HTML</h1> <p>Hello, World!</p>")
}

async fn render_form() -> Html<&'static str> {
    Html(r#"
    <html>
    <head>
        <title>Form Example</title>
    </head>
    <body>
        <h1>Form Example</h1>
        <form method="post">
            <label for="field1">姓名:</label>
            <input type="text" name="field1" id="field1"><br>

            <label for="field2">年龄:</label>
            <input type="text" name="field2" id="field2"><br>

            <input type="submit" value="Submit">
        </form>
    </body>
    </html>
"#)
}

#[derive(Deserialize)]
struct FormData {
    field1: String,
    field2: String,
}

#[derive(Debug)]
struct FormChunkData {
    total_number: u64,
    chunk_size: u64,
    chunk_number: u64,
    md5: String,
    bytes: axum::body::Bytes,
    file_name: String,
    total_size: u64,
}

impl FormChunkData {
    fn new() -> FormChunkData {
        FormChunkData {
            total_number: 0,
            chunk_size: 0,
            chunk_number: 0,
            md5: String::new(),
            bytes: axum::body::Bytes::new(),
            file_name: String::new(),
            total_size: 0,
        }
    }
}

#[derive(Debug)]
struct TransferInfo {
    file_name: String,
    total_number: u64,
    start_time: SystemTime,
    chunk: HashMap<u64,bool>
}

impl TransferInfo {
    fn new(name: String,total: u64,start_time: SystemTime) -> TransferInfo {
        let mut chunk = HashMap::new();
        for i in 0..total {
            chunk.insert(i, false);
        }
        TransferInfo {
            file_name: name,
            total_number: total,
            start_time: start_time,
            chunk: chunk,
        }
    }

    fn complated(&self) -> bool {
        for i in self.chunk.values() {
            if !i {
                return false;
            }
        }
        true
    }
}

static mut TRANSFER_INFO_LIST: Lazy<Mutex<HashMap<String,TransferInfo>>> = Lazy::new(|| {
    Mutex::new(HashMap::new())
});

#[derive(Serialize,Deserialize)]
struct ChunkResult {
    file_name: String,
    chunk_number: u64,
    chunk_size: u64,
    res: bool,
    completed: bool,
}

async fn handle_form_submit(Form(form_data): Form<FormData>) -> Html<String> {
    let response_html = format!(
        r#"
        <html>
        <head>
            <title>Form Submission Result</title>
        </head>
        <body>
            <h1>Form Submission Result</h1>
            <p>Field 1: {}</p>
            <p>Field 2: {}</p>
        </body>
        </html>
    "#,
        form_data.field1, form_data.field2
    );

    Html(response_html)
}

#[derive(Template)]
#[template(path = "upload.html")]
 struct UploadTemplate {
}

/// 上传表单
async fn upload_file() -> Html<String> {

    let tpl = UploadTemplate{};
    let html = tpl.render().map_err(|err| err.to_string()).unwrap();
    Html(html)
}

//文件分块上传
async fn upload_file_action_chunk(mut multipart: Multipart) -> Json<Value> {
    let mut chunk_data = FormChunkData::new();
    let now = SystemTime::now();
    while let Some(field) = multipart.next_field().await.unwrap() {

        let name = field.name().unwrap().to_string();
        if let Some(file_name) = field.file_name() {
            chunk_data.file_name = file_name.to_string();
        }
        match name.as_str() {
            "md5" => chunk_data.md5 = field.text().await.unwrap(),
            "totalNumber" => chunk_data.total_number = field.text().await.unwrap().parse().unwrap(),
            "chunkSize" => chunk_data.chunk_size = field.text().await.unwrap().parse().unwrap(),
            "chunkNumber" => chunk_data.chunk_number = field.text().await.unwrap().parse().unwrap(),
            "totalSize" => chunk_data.total_size = field.text().await.unwrap().parse().unwrap(),
            "file" => {
                chunk_data.bytes = field.bytes().await.unwrap();
                // println!("chunk bytes len:{}",chunk_data.bytes.len());
            },
            _ => ()
        }
    }

    unsafe {
        let mut exists = false;
        match TRANSFER_INFO_LIST.lock().unwrap().get(&chunk_data.file_name) {
            Some(_) => exists = true,
            None => exists = false,
        } 

        if !exists {
            TRANSFER_INFO_LIST.lock().unwrap()
            .insert(chunk_data.file_name.clone(), 
                    TransferInfo::new(chunk_data.file_name.clone(), chunk_data.total_number,now)
            );
            // println!("transfer:{:?}",TRANSFER_INFO_LIST.lock().unwrap().get(&chunk_data.file_name));
        }
    }

    println!("处理文件:《{}》,总大小:{},总块数:{},当前块:{},当前块大小:{}",
        chunk_data.file_name,chunk_data.total_size,chunk_data.total_number,chunk_data.chunk_number,chunk_data.chunk_size);

    let path_exists = Path::new(&chunk_data.file_name).exists();
    let mut file = std::fs::OpenOptions::new()
            .read(true)
            .write(true)
            .create(true)
            // .append(true)        //文件随机访问不能打开这个功能
            .open(&chunk_data.file_name)
            .unwrap();

    if(!path_exists){
        file.set_len(chunk_data.total_size).unwrap();
    }

    let mut result: ChunkResult;

    let mut mmap = unsafe { MmapMut::map_mut(&file).unwrap() };
    let start = (chunk_data.chunk_number * MAX_CHUNK_SIZE) as usize;
    let end = (chunk_data.chunk_number * MAX_CHUNK_SIZE + chunk_data.chunk_size - 1) as usize;
    match (&mut mmap[start..=end]).write_all(&chunk_data.bytes) {
        Ok(_) => {
            // println!("文件《{}》第{}块写入位置:{}-{} 大小:{} 成功",chunk_data.file_name,chunk_data.chunk_number,start,end,chunk_data.bytes.len());
            unsafe {
                TRANSFER_INFO_LIST.lock().unwrap().get_mut(&chunk_data.file_name).unwrap().chunk.insert(chunk_data.chunk_number, true);
                println!("now transfer:{:?}",TRANSFER_INFO_LIST.lock().unwrap().get(&chunk_data.file_name));
                result = ChunkResult {
                    file_name: chunk_data.file_name.clone(),
                    chunk_number: chunk_data.chunk_number,
                    res: true,
                    chunk_size: chunk_data.chunk_size,
                    completed: false
                };
                let complated = TRANSFER_INFO_LIST.lock().unwrap().get(&chunk_data.file_name).unwrap().complated();
                if complated {
                    let new_sys_time = SystemTime::now();
                    let difference = new_sys_time.duration_since(TRANSFER_INFO_LIST.lock().unwrap().get(&chunk_data.file_name).unwrap().start_time).unwrap();

                    println!("文件《{}》已经传输完成!!耗时:{}毫秒",chunk_data.file_name,difference.as_millis());
                    let file_md5 = md5(&chunk_data.file_name).unwrap();
                    println!("文件《{}》MD5:{}",chunk_data.file_name,file_md5);
                    result.completed = true;
                 //   TRANSFER_INFO_LIST.lock().unwrap().remove(&chunk_data.file_name).unwrap();
                }
                
            }
        },
        Err(e) => {
            result = ChunkResult {
                file_name: chunk_data.file_name.clone(),
                chunk_number: chunk_data.chunk_number,
                res: false,
                chunk_size: chunk_data.chunk_size,
                completed: false
            };
            panic!("文件《{}》第{}块写入位置:{}-{} 大小:{} 失败，{:?}",chunk_data.file_name,chunk_data.chunk_number,start,end,chunk_data.bytes.len(),e);
        }
    };
    mmap.flush().unwrap();

    Json(json!(serde_json::to_string(&result).unwrap()))
}

fn md5<P: AsRef<Path>>(path: P) -> Result<String, std::io::Error> {
    let mut hasher = Md5::new();
    get_hash_file(path, &mut hasher)
}

/// 文件完整上传
async fn upload_file_action(mut multipart: Multipart) -> Result<(HeaderMap, String), String> {
    let mut res_string = String::new();
    while let Some(field) = multipart.next_field().await.unwrap() {

        let name = field.name().unwrap().to_string();

        let file_name = field.file_name().expect("xxxx").to_string();
        println!("处理{}",file_name);
        let content_type = field.content_type().unwrap().to_string();

        let data = field.bytes().await.unwrap();

        if file_name.len() <= 0 {
            continue;
        }

        println!("Length of `{name}` (`{file_name}`: `{content_type}`) is {} bytes",data.len());

        // 保存上传的文件
        //std::fs::write(&filename, &data).map_err(|err| err.to_string())?;
        tokio::fs::write(&file_name, &data)
            .await
            .map_err(|err| err.to_string())?;


            res_string = format!(
            "{}\r\n文件名：{:?}, 文件大小：{}",
            res_string,
            file_name,
            data.len());
    }
    if res_string.len() <= 0 {
        res_string = "没有上传文件".to_owned();
    } else {
        res_string = format!("【上传的文件】\r\n {}",res_string);
    }
    cn(res_string).await
}

/// 中文响应
async fn cn(msg: String) -> Result<(HeaderMap, String), String> {
    let mut headers = HeaderMap::new();
    headers.insert(
        axum::http::header::CONTENT_TYPE,
        "text/plain;charset=utf-8".parse().unwrap(),
    );
    Ok((headers, msg))
}
